/*
 * Copyright (c) 2023 Cshoo Org. All Rights Reserved.
 */

package org.cshoo.tattoo.gate.config;

import lombok.extern.slf4j.Slf4j;
import org.cshoo.tattoo.gate.handler.JsonServerAuthenticationFailureHandler;
import org.cshoo.tattoo.gate.handler.MyRedirectServerAuthenticationSuccessHandler;
import org.cshoo.tattoo.gate.service.MyReactiveUserDetailService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.authentication.DelegatingReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.LinkedList;

/**
 * @author 杨翼
 * @since 1.0
 */
@Configuration
@EnableWebFluxSecurity
@Slf4j
public class SecurityConfig {

    private static final String[] AUTH_WHITELIST = new String[]{"/login", "/logout", "/token/refresh"};

    @Resource
    private MyRedirectServerAuthenticationSuccessHandler myRedirectServerAuthenticationSuccessHandler;

    @Resource
    private JsonServerAuthenticationFailureHandler jsonServerAuthenticationFailureHandler;

    @Resource
    private MyReactiveUserDetailService reactiveUserDetailsService;

    @Resource
    private MyReactiveAuthorizationManager reactiveAuthorizationManager;

    @Bean
    public ReactiveAuthenticationManager reactiveAuthenticationManager() {
        final ReactiveUserDetailsService detailsService = reactiveUserDetailsService;
        LinkedList<ReactiveAuthenticationManager> managers = new LinkedList<>();
        managers.add(authentication -> Mono.empty());
        UserDetailsRepositoryReactiveAuthenticationManager manager = new UserDetailsRepositoryReactiveAuthenticationManager(detailsService);
        manager.setPasswordEncoder(new BCryptPasswordEncoder());
        managers.add(manager);
        return new DelegatingReactiveAuthenticationManager(managers);
    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {

        http.exceptionHandling().authenticationEntryPoint((exchange, ex) -> Mono.fromRunnable(() -> {
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.FORBIDDEN);
        }));

        return http.formLogin()
                .authenticationSuccessHandler(myRedirectServerAuthenticationSuccessHandler)
                .authenticationFailureHandler(jsonServerAuthenticationFailureHandler)
                .and()
                .authorizeExchange()
                .pathMatchers(AUTH_WHITELIST).permitAll()
                .anyExchange().access(reactiveAuthorizationManager)
                .and()
                .csrf()
                .disable()
                .httpBasic()
                .disable()
                .build();
    }

}
